package com.dy.yunying.biz.service.manage.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dy.yunying.api.constant.ChannelConstants;
import com.dy.yunying.api.constant.ChannelPackStatusEnum;
import com.dy.yunying.api.constant.ChannelPackTypeEnum;
import com.dy.yunying.api.constant.PackConstants;
import com.dy.yunying.api.dto.ChannelManageDto;
import com.dy.yunying.api.dto.PackChannelInfoDto;
import com.dy.yunying.api.entity.*;
import com.dy.yunying.api.enums.ExtendPackageEnum;
import com.dy.yunying.api.req.*;
import com.dy.yunying.api.resp.ChannelPackRes;
import com.dy.yunying.api.vo.ChannelManageVo;
import com.dy.yunying.api.vo.WanChannelPackPageVO;
import com.dy.yunying.api.vo.WanChannelPackVO;
import com.dy.yunying.api.vo.WanGameVersionVO;
import com.dy.yunying.biz.config.*;
import com.dy.yunying.biz.dao.ads.GdtAppMapper;
import com.dy.yunying.biz.dao.ads.WanChannelPackRuleMapper;
import com.dy.yunying.biz.dao.manage.ext.ChannelManageDOMapperExt;
import com.dy.yunying.biz.dao.manage.ext.TtExtendPackageMapperExt;
import com.dy.yunying.biz.dao.manage.ext.WanChannelPackDOMapperExt;
import com.dy.yunying.biz.service.manage.*;
import com.dy.yunying.biz.utils.CDNUtil;
import com.dy.yunying.biz.utils.DateUtils;
import com.dy.yunying.biz.utils.OEHttpUtils;
import com.dy.yunying.biz.utils.OkHttpUtil;
import com.google.common.collect.Lists;
import com.huaweicloud.sdk.cdn.v1.model.RefreshTaskRequestBody;
import com.pig4cloud.pig.admin.api.entity.SysUser;
import com.pig4cloud.pig.admin.api.feign.RemoteUserService;
import com.pig4cloud.pig.admin.api.vo.UserSelectVO;
import com.pig4cloud.pig.api.entity.GdtChannelPackage;
import com.pig4cloud.pig.api.entity.ResponseBean;
import com.pig4cloud.pig.api.entity.TtAppManagement;
import com.pig4cloud.pig.api.enums.GdtChannelPackageEnum;
import com.pig4cloud.pig.api.feign.RemoteConvertService;
import com.pig4cloud.pig.api.feign.RemoteTtAccesstokenService;
import com.pig4cloud.pig.api.vo.GdtChannelPackageReq;
import com.pig4cloud.pig.api.vo.TtExtendPackageVO;
import com.pig4cloud.pig.common.core.constant.CommonConstants;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.core.constant.enums.PlatformTypeEnum;
import com.pig4cloud.pig.common.core.exception.BusinessException;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.security.util.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.apache.commons.text.StringSubstitutor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.google.common.collect.Lists.newArrayList;

/**
 * 游戏版本
 *
 * @Author: hjl
 * @Date: 2020/7/21 13:52
 */
@Slf4j
@Transactional
@Service
public class GameChannelPackServiceImpl implements GameChannelPackService {

	@Autowired
	private WanChannelPackDOMapperExt wanChannelPackDOMapper;

	@Autowired
	private GameService gameService;

	@Autowired
	private GameVersionService gameVersionService;

	@Autowired
	private ChannelManageService channelManageService;

	@Autowired
	private GameChannelPlatformService gameChannelPlatformService;

	@Autowired
	private WanGameChannelInfoService wanGameChannelInfoService;

	@Autowired
	private ParentGameVersionService parentGameVersionService;

	@Autowired
	private PromotionChannelService promotionChannelService;
	@Autowired
	private AdvertiseMonitorService adService;
	@Autowired
	private ChannelManageDOMapperExt channelManageDOMapperExt;

	@Autowired
	private RemoteUserService remoteUserService;

	@Autowired
	private AdRoleUserService adRoleUserService;

	@Autowired
	private RemoteTtAccesstokenService remoteTtAccesstokenService;

	@Autowired
	private TtExtendPackageService ttExtendPackageService;

	@Autowired
	private TtExtendPackageMapperExt ttExtendPackageMapperExt;

	@Autowired
	private GdtAppMapper gdtAppMapper;

	@Autowired
	private ChannelPackConfig channelPackConfig;
	@Autowired
	private RemoteConvertService remoteConvertService;

	@Autowired
	private ToutiaoConfig toutiaoConfig;

	@Autowired
	private YunYingProperties yunYingProperties;
	@Autowired
	private WanChannelPackRuleMapper wanChannelPackRuleMapper;

	@Override
	public IPage<WanChannelPackPageVO> page(WanChannelPackReq req) {

		IPage<WanChannelPackPageVO> page = wanChannelPackDOMapper.page(req);

		if (page != null && CollectionUtils.isNotEmpty(page.getRecords())) {

			R<List<SysUser>> r = remoteUserService.getUserList(SecurityConstants.FROM_IN);
			Map<String, SysUser> map = new HashMap<>();
			if (r.getCode() == CommonConstants.SUCCESS) {
				List<SysUser> list = r.getData();
				//map = list.stream().filter(x -> x != null && StringUtils.isNotBlank(x.getRealName())).collect(Collectors.toMap(
				map = list.stream().collect(Collectors.toMap(
						o -> {
							return String.valueOf(o.getUserId());
						}, Function.identity()));
			}

			List<String> packList = page.getRecords().stream().map(WanChannelPackPageVO::getCode).collect(Collectors.toList());
			// 获取广点通 gdt_channel_packages 表同步状态
			R<List<GdtChannelPackage>> resultChannelPackageList = remoteConvertService.getPackageList(new GdtChannelPackageReq().setAppChlList(packList), SecurityConstants.FROM_IN);


			// 获取回传规则列表
			List<WanChannelPackRule> ruleList = CollectionUtils.isNotEmpty(packList)? wanChannelPackRuleMapper.selectAllListByCodes(packList):new ArrayList<>();

			for (WanChannelPackPageVO wanChannelPackVO : page.getRecords()) {
				String creator = wanChannelPackVO.getCreator();
				wanChannelPackVO.setRuleList(ruleList.stream().filter(a -> a.getDeleted()==0 && a.getPackCode().equals(wanChannelPackVO.getCode())).collect(Collectors.toList()));
				SysUser user = map.get(creator);
				if (user != null) {
					wanChannelPackVO.setCreatorName(StringUtils.isBlank(user.getRealName()) ? user.getUsername() : user.getRealName());
				} else {
					wanChannelPackVO.setCreatorName(creator);
				}

				String path = wanChannelPackVO.getPath();
				if (StringUtils.isNotBlank(path)) {
					wanChannelPackVO.setUrl(channelPackConfig.getApp_file_url_root() + path);
				} else {
					wanChannelPackVO.setUrl("");
				}
				if (PlatformTypeEnum.TT.getValue().equals(wanChannelPackVO.getPlatform())) {
					String ttStatus = wanChannelPackVO.getTtStatus();
					wanChannelPackVO.setTtStatus(ExtendPackageEnum.getName(ttStatus));
				} else if (PlatformTypeEnum.GDT.getValue().equals(wanChannelPackVO.getPlatform())){
					// gdt_channel_packages 表同步状态
					List<GdtChannelPackage> gdtChannelPackageList = resultChannelPackageList.getData();
					if (CollectionUtils.isNotEmpty(gdtChannelPackageList)) {
						for (GdtChannelPackage gdtChannelPackage : gdtChannelPackageList) {
							if (gdtChannelPackage.getAppChl().equals(wanChannelPackVO.getCode())) {
								wanChannelPackVO.setTtStatus(GdtChannelPackageEnum.getName(gdtChannelPackage.getPackageStatus()));
							}
						}
					}
					wanChannelPackVO.setTtStatus(StringUtils.isNotBlank(wanChannelPackVO.getTtStatus()) ? wanChannelPackVO.getTtStatus() : "-");
				}
			}
		}
		return page;
	}

	@Override
	public WanChannelPackVO getVOByPK(Long packId) {
		return wanChannelPackDOMapper.getVOByPK(packId);
	}

	@Override
	public void pack(GameChannelPackReq req) {
		try {
			Long gameId = req.getGameId();
			Integer packType = req.getPackType();
			WanGameVersionVO game = gameService.selectVersionVOByPK(gameId, packType);
			if (game == null || game.getVersionId() == null) {
				log.error("游戏基本包不存在");
				throw new BusinessException(11, "游戏基本包不存在");
			}
			req.setCreator(String.valueOf(SecurityUtils.getUser().getId()));
			int channelId = req.getChlId();
			Long versionId = game.getVersionId();
			GameVersionDO gameVersion = gameVersionService.getByPK(versionId);
			if (gameVersion == null) {
				log.error("版本基本包不存在");
				throw new BusinessException(12, "版本基本包不存在");
			}
			ChannelManageVo channelVO = channelManageService.getVOByPK(channelId);
			if (channelVO == null) {
				log.error("渠道不存在");
				throw new BusinessException(13, "渠道不存在");
			}
			String chlCode = channelVO.getChncode();

			List<String> packCodeList = new ArrayList<>();
			for (int i = req.getPackNumFrom(); i <= req.getPackNumTo(); i++) {
				// 拼接  “渠道包编码”
				String packCode = chlCode + ChannelConstants.CHANNEL_PACK_SEP + gameId + ChannelConstants.CHANNEL_PACK_SEP + i;
				packCodeList.add(packCode);
			}
			this.batchPackAsync(gameVersion, channelVO, packCodeList, req);
		} catch (BusinessException e) {
			log.error("分包失败", e.getMessage());
			throw new BusinessException(1, e.getMessage());
		} catch (Exception e) {
			log.error("分包失败", e);
			throw new BusinessException(1, "分包失败");
		}
	}

	/**
	 * 调用文件服务器上的打包服务(异步：使用线程池调用)
	 *
	 * @param gameVersion
	 * @param channelVO
	 * @param packCodeList
	 * @return
	 */
	@Override
	public void batchPackAsync(GameVersionDO gameVersion, ChannelManageVo channelVO, List<String> packCodeList, GameChannelPackReq req) {
		// 创建线程池对象
		ExecutorService service = Executors.newFixedThreadPool(PackConstants.CHANNEL_PACK_THREAD_FIXED_NUM); // 线程并发度
		try {
			if (gameVersion == null) {
				log.error("游戏信息缺失");
				throw new BusinessException(11, "游戏不存在");
			}
			if (channelVO == null || CollectionUtils.isEmpty(packCodeList)) {
				log.error("渠道信息缺失");
				throw new BusinessException(12, "渠道信息不存在");
			}

			String remark = req.getRemark();
			String creator = req.getCreator();
			Long gameId = gameVersion.getGameId();
			// 获取主渠道信息
			ChannelManageVo parentChl = channelManageService.getVOByPK(channelVO.getPid());
			Integer platform = parentChl.getPlatform();

			// 获取游戏-平台上报信息
			WanGameChannelPlatformDO wanGameChannelPlatformDO = gameChannelPlatformService.getByPlatformAndGameid(platform, gameId.intValue(), null);

			// 分批处理
			List<String> packCodeListTmp = new ArrayList<>();
			for (String packCode : packCodeList) {
				// 创建分包记录
				WanChannelPackDO wanChannelPackDO = new WanChannelPackDO();
				wanChannelPackDO.setGameId(gameId);
				wanChannelPackDO.setVersionId(gameVersion.getVersionId());
				wanChannelPackDO.setType(req.getType());
				wanChannelPackDO.setPackType(gameVersion.getPackType());
				wanChannelPackDO.setCode(packCode);
				wanChannelPackDO.setChlCode(channelVO.getChncode());
				wanChannelPackDO.setPath("");
				wanChannelPackDO.setCreator(creator);
				wanChannelPackDO.setStatus(ChannelPackStatusEnum.GENERATING.getStatus());
				if (null != req.getSpreadType() && 2 == req.getSpreadType()) {
					wanChannelPackDO.setSpreadType(req.getSpreadType());
					wanChannelPackDO.setSettleType(req.getSettleType());
				}
				this.save(wanChannelPackDO);

				packCodeListTmp.add(packCode);

				if (packCodeListTmp.size() >= PackConstants.CHANNEL_PACK_BATCH_NUM) {
					GameChannelPackServiceImpl.SyncPackThread syncPackThread = new GameChannelPackServiceImpl.SyncPackThread(gameVersion, parentChl, channelVO, wanGameChannelPlatformDO, packCodeListTmp, remark, creator);
					service.submit(syncPackThread);
					packCodeListTmp = new ArrayList<>();
				}
			}
			if (packCodeListTmp.size() > 0) {
				GameChannelPackServiceImpl.SyncPackThread syncPackThread = new GameChannelPackServiceImpl.SyncPackThread(gameVersion, parentChl, channelVO, wanGameChannelPlatformDO, packCodeListTmp, remark, creator);
				service.submit(syncPackThread);
			}

			if (wanGameChannelPlatformDO == null) {
				throw new BusinessException(12, "盘古平台包已生成,但渠道参数未配置");
			}

			if (1 == platform && wanGameChannelPlatformDO != null && req.getType() != 2) {
				Long appid = wanGameChannelPlatformDO.getAppid();
				// 根据appid 查询对应的应用包id和广告账户
				TtAppManagement ttAppManagement = ttExtendPackageMapperExt.getPackageIdByAppId(String.valueOf(appid));
				if (ttAppManagement != null) {
					// 调用头条API《创建应用分包》及分包列表
					tTExtendPackage(req, packCodeList, parentChl, wanGameChannelPlatformDO, ttAppManagement);
				} else {
					throw new BusinessException(12, "盘古平台包已生成,但应用未同步媒体包未生成");
				}
			}

			if (8 == platform && wanGameChannelPlatformDO != null && req.getType() != 2) {
				// 获取创建者广告账户和appid
				// 调用广点通创建应用分包
				R result = this.gdtExtendPackage(wanGameChannelPlatformDO, packCodeList);
				if (0 != result.getCode()){
					throw new BusinessException(12, result.getMsg());
				}
			} else if (10 == platform) {
				// 快手没有分包接口 目前是按子游戏维度创建转化时调用快手创建应用的接口
			}

		} catch (BusinessException e) {
			log.error("生成渠道分包失败", e);
			throw new BusinessException(12, e.getMessage());
		} catch (Exception e) {
			log.error("生成渠道分包失败", e);
			throw new BusinessException(12, "生成渠道分包失败");
		} finally {
			//注意：submit方法调用结束后，程序并不终止，是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
			//关闭线程池
			service.shutdown();
		}
	}

//	public static void main(String[] args) {
//		// 调用广点通创建应用分包
//		String accessToken = "920e0daded186311a3252748f9bf7666";
//		try {
//			Map<String, String> map = new HashMap<>();
//			map.put("access_token", accessToken);
//			map.put("timestamp", String.valueOf(System.currentTimeMillis()/1000));
//			map.put("nonce", RandomUtil.generateLetterStr(32));
//			String url = "https://api.e.qq.com/v1.3/extend_package/add" + "?" + MapUtils.queryString(map);
//
//			Map<String, Object> data = new HashMap<>();
//			data.put("account_id", "17209785");
//			data.put("package_id", "2000000264");
//
//			List channel_list = Lists.newArrayList();
//			Map<String, String> channel_map = new HashMap<>();
//			channel_map.put("channel_id", "11x22x33x44");
//			channel_list.add(channel_map);
//			data.put("channel_list", channel_list);
//
//			String result = OEHttpUtils.doPost(url, data);
//			System.out.println(result);
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
//	}

	/**
	 * 调用头条API《创建应用分包》及分包列表
	 *
	 * @param req
	 * @param packCodeList
	 * @param parentChl
	 */
	private void tTExtendPackage(GameChannelPackReq req, List<String> packCodeList, ChannelManageVo parentChl, WanGameChannelPlatformDO wanGameChannelPlatformDO, TtAppManagement ttAppManagement) {
		// 根据子游戏主渠道 查找到对应生成渠道参数配置的 appid
		Long appid = wanGameChannelPlatformDO.getAppid();
		//取应用的appName
		String appname = ttAppManagement.getAppName();
		Long gameId = req.getGameId();
		Integer platform = parentChl.getPlatform();
		String parentCode = req.getParentCode();

		String advertiserId = ttAppManagement.getAdvertiserId();
		String packageId = ttAppManagement.getPackageId();
		// 调用头条API《创建应用分包》
		R dataResult = remoteTtAccesstokenService.fetchAccesstoken(SecurityConstants.FROM_IN, advertiserId);

		String accessToken = (String) dataResult.getData();

		// 头条创建分包url
		String url = "https://ad.oceanengine.com/open_api/2/tools/app_management/extend_package/create/";

		// 头条分包明细url
		String urlDetail = "https://ad.oceanengine.com/open_api/2/tools/app_management/extend_package/list/";

		List<TtExtendPackageVO> successList = new ArrayList<>();
		List<String> errorList = new ArrayList<>();

		// 保存头条创建记录
		List<TtExtendPackageDO> extendPackageDOList = newArrayList();
		packCodeList.forEach(packCode -> {
			// 判断分包id在 分包列表里面是否存在
			TtExtendPackageDO ttExtendPackageDO = ttExtendPackageService.getOne(Wrappers.<TtExtendPackageDO>query()
					.lambda().in(TtExtendPackageDO::getChannelId, packCode).eq(TtExtendPackageDO::getIsDeleted, 0).last("LIMIT 1"));
			// 没有创建就去创建
			if (ObjectUtils.isEmpty(ttExtendPackageDO)) {
				TtExtendPackageVO ttExtendPackageVO = new TtExtendPackageVO();
				ttExtendPackageVO.setChannel_id("channel_id");
				ttExtendPackageVO.setChlCode(packCode);
				successList.add(ttExtendPackageVO);
				TtExtendPackageDO extendPackageDO = new TtExtendPackageDO();
				extendPackageDO.setAdvertiserId(advertiserId);
				extendPackageDO.setPackageId(packageId);
				extendPackageDO.setGameid(gameId.intValue());
				extendPackageDO.setAppId(String.valueOf(appid));
				extendPackageDO.setAppName(appname);
				extendPackageDO.setParentchl(parentCode);
				extendPackageDO.setPlatform(platform);
				extendPackageDO.setChannelId(packCode);
				extendPackageDO.setIsDeleted(0);
				extendPackageDO.setCreateTime(new Date());
				Integer userId = SecurityUtils.getUser().getId();
				extendPackageDO.setCreateId(Long.valueOf(userId));
				extendPackageDO.setUpdateId(Long.valueOf(userId));
				extendPackageDO.setUpdateTime(new Date());
				extendPackageDOList.add(extendPackageDO);
			}
			// 有就不去头条创建
			if (ttExtendPackageDO != null) {
				errorList.add(packCode);
			}
		});
		// 去头条创建分包
		if (ObjectUtils.isNotEmpty(successList) || successList.size() > 0) {

			List<Map<String, String>> mapList = new ArrayList<>();
			successList.forEach(atm -> {
				Map<String, String> atmMap = new HashMap<String, String>();
				atmMap.put(atm.getChannel_id(), atm.getChlCode());
				mapList.add(atmMap);
			});

			String param = JSONObject.toJSONString(mapList);

			Map<String, Object> data = new HashMap<String, Object>();
			data.put("advertiser_id", advertiserId);
			data.put("package_id", packageId);
			data.put("mode", "Manual");
			data.put("channel_list", param);

			//调用头条创建接口
			String resultStr = OEHttpUtils.doPost(url, data, accessToken);

			ResponseBean resBean = JSON.parseObject(resultStr, ResponseBean.class);
			if (resBean != null && "0".equals(resBean.getCode())) {
				// 返回成功 就把此创建记录 保存到数据库
				ttExtendPackageMapperExt.saveBatchExtendPackage(extendPackageDOList);

				// 创建成功后调用头条

				Map<String, Object> datalist = new HashMap<String, Object>();
				datalist.put("advertiser_id", advertiserId);
				datalist.put("package_id", packageId);
				// 调用头条分包明细接口
				String result = OEHttpUtils.doGet(urlDetail, datalist, accessToken);
				ResponseBean resBeanDetail = JSON.parseObject(result, ResponseBean.class);
				if (resBeanDetail != null && "0".equals(resBeanDetail.getCode())) {
					JSONObject jsonObj = resBeanDetail.getData();
					JSONArray arr = JSON.parseArray(String.valueOf(jsonObj.get("list")));
					if (arr.size() > 0) {
						for (Object obj : arr) {
							JSONObject jsonObject = (JSONObject) obj;

							TtExtendPackageDO ttExtendPackageDO = new TtExtendPackageDO();
							ttExtendPackageDO.setReason(jsonObject.getString("reason"));
							ttExtendPackageDO.setStatus(jsonObject.getString("status"));
							ttExtendPackageDO.setTtUpdateTime(jsonObject.getString("update_time"));
							ttExtendPackageDO.setVersionName(jsonObject.getString("version_name"));

							ttExtendPackageDO.setChannelId(jsonObject.getString("channel_id"));
							ttExtendPackageDO.setDownloadUrl(jsonObject.getString("download_url"));
							String channel_id = jsonObject.getString("channel_id");

							TtExtendPackageDO extendPackageDO = ttExtendPackageService.getOne(Wrappers.<TtExtendPackageDO>query()
									.lambda().in(TtExtendPackageDO::getChannelId, channel_id).eq(TtExtendPackageDO::getIsDeleted, 0).last("LIMIT 1"));
							// 新增
							if (extendPackageDO == null) {
								ttExtendPackageDO.setIsDeleted(0);
								ttExtendPackageDO.setAdvertiserId(advertiserId);
								ttExtendPackageDO.setPackageId(packageId);
								ttExtendPackageDO.setGameid(gameId.intValue());
								ttExtendPackageDO.setAppId(String.valueOf(appid));
								ttExtendPackageDO.setAppName(appname);
								ttExtendPackageDO.setParentchl(parentCode);
								ttExtendPackageDO.setPlatform(platform);
								ttExtendPackageDO.setCreateTime(new Date());
								Integer userId = SecurityUtils.getUser().getId();
								ttExtendPackageDO.setCreateId(Long.valueOf(userId));
								ttExtendPackageDO.setUpdateId(Long.valueOf(userId));
								ttExtendPackageDO.setUpdateTime(new Date());
								ttExtendPackageMapperExt.saveExtendPackage(ttExtendPackageDO);

							} else {
								ttExtendPackageMapperExt.updateByChannelId(ttExtendPackageDO);
							}

						}
					}
				}
			} else {
				log.error("调用分包接口返回："+resultStr);
				throw new BusinessException(12, resBean.getMessage());
			}
		}

	}

	public WanChannelPackDO save(WanChannelPackDO wanChannelPackDO) {
		// 1、保存渠道包分包记录
		wanChannelPackDO.setCreateTime(DateUtils.getCurrentDate());
		wanChannelPackDOMapper.insertSelective(wanChannelPackDO);

		// 2、保存渠道包基本信息
		WanGameChannelInfoDO wanGameChannelInfoDO = new WanGameChannelInfoDO();
		wanGameChannelInfoDO.setGameid(wanChannelPackDO.getGameId().intValue());
		wanGameChannelInfoDO.setChl(wanChannelPackDO.getCode());

		//分包名称添加
		wanGameChannelInfoDO.setCodeName(wanChannelPackDO.getCode());
		wanGameChannelInfoDO.setChannel(wanChannelPackDO.getChlCode());
		wanGameChannelInfoDO.setSpreadType(wanChannelPackDO.getSpreadType());
		wanGameChannelInfoDO.setSettleType(wanChannelPackDO.getSettleType());
		wanGameChannelInfoService.save(wanGameChannelInfoDO);
		return wanChannelPackDO;
	}

	/**
	 * 多线程同步分包
	 */
	class SyncPackThread implements Callable<List<WanChannelPackVO>> {

		private GameVersionDO gameVersion;
		private ChannelManageVo parentChl;
		private ChannelManageVo channelVO;
		private WanGameChannelPlatformDO wanGameChannelPlatformDO;
		private List<String> packCodeList;
		private String remark;
		private String creator;

		public SyncPackThread(GameVersionDO gameVersion, ChannelManageVo parentChl, ChannelManageVo channelVO, WanGameChannelPlatformDO wanGameChannelPlatformDO,
							  List<String> packCodeList, String remark, String creator) {
			this.gameVersion = gameVersion;
			this.parentChl = parentChl;
			this.channelVO = channelVO;
			this.wanGameChannelPlatformDO = wanGameChannelPlatformDO;
			this.packCodeList = packCodeList;
			this.remark = remark;
			this.creator = creator;
		}

		@Override
		public List<WanChannelPackVO> call() throws Exception {
			return packSync(gameVersion, parentChl, channelVO, wanGameChannelPlatformDO, packCodeList, remark);
		}
	}

	/**
	 * 调用文件服务器上的打包服务(同步)
	 *
	 * @param gameVersion
	 * @param channelVO
	 * @param packCodeList
	 * @return
	 */
	private List<WanChannelPackVO> packSync(GameVersionDO gameVersion, ChannelManageVo parentChl, ChannelManageVo channelVO, WanGameChannelPlatformDO wanGameChannelPlatformDO,
											List<String> packCodeList, String remark) {
		List<WanChannelPackVO> wanChannelPackVOList = new ArrayList<>();
		if (gameVersion == null) {
			log.error("游戏信息缺失");
			throw new BusinessException(11, "游戏不存在");
		}
		if (parentChl == null || channelVO == null || CollectionUtils.isEmpty(packCodeList)) {
			log.error("渠道信息缺失");
			throw new BusinessException(12, "渠道信息缺失");
		}
		Long gameId = gameVersion.getGameId();
		try {
			Integer platform = parentChl.getPlatform();
			Integer rtype = channelVO.getRtype();
//            Integer isup = channelVO.getIsup();
//            Integer paytype = channelVO.getPaytype();

			// 获取游戏-平台上报信息
			Long appid = null;
			String appname = null;
			if (wanGameChannelPlatformDO != null) {
				appid = wanGameChannelPlatformDO.getAppid();
				appname = wanGameChannelPlatformDO.getAppname();
			}

			List<PackChannelInfoDto> channelInfoList = new ArrayList<>();
			for (String packCode : packCodeList) {
				// 拼装分包参数
				PackChannelInfoDto packChannelInfoDto = new PackChannelInfoDto();
				packChannelInfoDto.setPlatform(platform);
				packChannelInfoDto.setRtype(rtype);
				packChannelInfoDto.setAppId(appid == null ? "" : String.valueOf(appid));
				packChannelInfoDto.setAppName(appname);
				packChannelInfoDto.setChlCode(channelVO.getChncode());
				packChannelInfoDto.setAppChlCode(packCode);
				channelInfoList.add(packChannelInfoDto);
			}

			// TODO 使用 Map<String, Object> 和 JSON.toJSONString() 会导致 签名不一致
			Map<String, String> params = new HashMap<>();
			params.put("gameId", String.valueOf(gameVersion.getGameId()));
			params.put("versionId", String.valueOf(gameVersion.getVersionId()));
			params.put("packType", String.valueOf(gameVersion.getPackType()));
			params.put("sonGameDecodePath", gameVersion.getPath());
			params.put("chlCode", channelVO.getChncode());
			params.put("channelInfos", JSON.toJSONString(channelInfoList));
			// chengang mod 目前广点通下载链接使用的是盘古平台的，带上版本号不便于更新基础包
			params.put("pgName", platform != 8 ? gameVersion.getPgname() : "");
			params.put("vName", platform != 8 ? gameVersion.getName() : "");

			log.info("调用网关接口, params：{}", JSON.toJSONString(params));

			String result = OkHttpUtil.doPostForm(channelPackConfig.getPack_api_channel_batch_pack(), params);
			log.info("调用网关接口,返回result:{}", result);
			if (StringUtils.isNotEmpty(result)) {
				JSONObject jo = JSON.parseObject(result);
				Integer code = jo.getInteger("code");
				if (code != null) {
					if (code == 0) {
						JSONObject date = jo.getJSONObject("data");
						JSONArray channelPackResListArr = date.getJSONArray("channelPackResList");
						if (channelPackResListArr != null && !channelPackResListArr.isEmpty()) {
							List<ChannelPackRes> channelPackResList = channelPackResListArr.toJavaList(ChannelPackRes.class);
							channelPackResList.forEach(channelPackRes -> {
								String packCode = channelPackRes.getPackCode();
								WanChannelPackDO wanChannelPackDO = new WanChannelPackDO();
								wanChannelPackDO.setGameId(gameId);
								wanChannelPackDO.setVersionId(gameVersion.getVersionId());
								wanChannelPackDO.setCode(packCode);
								String path = channelPackRes.getPath();
								wanChannelPackDO.setPath(path == null ? "" : path);
								wanChannelPackDO.setApp_file_url_root(channelPackConfig.getApp_file_url_root());
								wanChannelPackDO.setStatus(ChannelPackStatusEnum.VALID.getStatus());
								wanChannelPackDO = updateByUK(wanChannelPackDO);
								packCodeList.remove(packCode);

								WanChannelPackVO wanChannelPackVO = new WanChannelPackVO();
								BeanUtils.copyProperties(wanChannelPackDO, wanChannelPackVO);
								wanChannelPackVOList.add(wanChannelPackVO);
							});
						}
					} else {
						String msg = jo.getString("msg");
						log.error("生成渠道包失败, msg:{}", msg);
					}
				} else {
					log.error("请求生成渠道包接口失败");
				}
			}
			return wanChannelPackVOList;
		} catch (BusinessException e) {
			log.error("生成渠道分包失败", e);
			throw new BusinessException(1, e.getMessage());
		} catch (Exception e) {
			log.error("生成渠道分包失败", e);
			throw new BusinessException(1, "生成渠道分包失败");
		} finally {
			// 未正常生成分包的修改状态为失败
			for (String packCode : packCodeList) {
				WanChannelPackDO wanChannelPackDO = new WanChannelPackDO();
				wanChannelPackDO.setGameId(gameId);
				wanChannelPackDO.setVersionId(gameVersion.getVersionId());
				wanChannelPackDO.setCode(packCode);
				wanChannelPackDO.setStatus(ChannelPackStatusEnum.FAIL.getStatus());
				updateByUK(wanChannelPackDO);
			}
		}
	}

	@Override
	public WanChannelPackDO updateByUK(WanChannelPackDO wanChannelPackDO) {
		wanChannelPackDO.setUpdateTime(DateUtils.getCurrentDate());
		wanChannelPackDOMapper.updateByUKSelective(wanChannelPackDO);

		// 包更新成功  刷新CDN
		if ("Y".equals(CDNConfig.cdn_refresh_flag)) {
			Integer status = wanChannelPackDO.getStatus();
			String code = wanChannelPackDO.getCode();
			String path = wanChannelPackDO.getPath();
			if (ChannelPackStatusEnum.VALID.getStatus() == status && StringUtils.isNotBlank(path)) {
				int count = wanChannelPackDOMapper.countByCode(code);
				// 已分包成功过
				if (count > 1) {
					// 刷新CDN
					refreshCDN(path);
				}
			}
		}
		return wanChannelPackDO;
	}

	/**
	 * 刷新CDN
	 *
	 * @param path
	 */
	private void refreshCDN(String path) {
		String url = channelPackConfig.getApp_file_url_root() + path;
		try {
			List<String> refreshTaskUrls = new ArrayList<>();
			refreshTaskUrls.add(url);
			CDNUtil.createRefreshTask(RefreshTaskRequestBody.TypeEnum.FILE, refreshTaskUrls);
			log.info("refreshCDN url=[{}] success. ", url);
		} catch (Throwable e) {
			log.error("refreshCDN url=[{}] error, ", url);
			log.error("refreshCDN error, ", e);
		}
	}

	@Override
	public List<String> getChlCodeByGameId(Long gameId) {
		return wanChannelPackDOMapper.getChlCodeByGameId(gameId);
	}

	@Override
	public void packBase(GameBasePackReq req) {
		try {
			Long gameId = req.getGameId();
			Integer parentChlId = req.getParentChlId();
			WanGameDO wanGameDO = gameService.selectByPrimaryKey(gameId);
			Long parentGameId = wanGameDO.getPgid();

			ParentGameVersionDO parentGameVersionDO = parentGameVersionService.getLatestByGameId(parentGameId, null);
			if (parentGameVersionDO == null) {
				log.error("父游戏母包不存在");
				throw new BusinessException(14, "父游戏母包不存在");
			}

			boolean updateBasePackFlag = true;
			WanGameVersionVO game = gameService.selectVersionVOByPK(gameId, null);
			if (game != null && game.getVersionId() != null) {
				// 子游戏基础包已更新
				if (game.getVersionCode() >= parentGameVersionDO.getCode()) {
					updateBasePackFlag = false;
				}
			}
			if (updateBasePackFlag) {
				// 更新子游戏基础包目录
				parentGameVersionService.updateBasePack(parentGameVersionDO, gameId);
			}

			game = gameService.selectVersionVOByPK(gameId, null);
			if (game == null || game.getVersionId() == null) {
				log.error("游戏基本包不存在");
				throw new BusinessException(11, "游戏基本包不存在");
			}

			Long versionId = game.getVersionId();
			GameVersionDO gameVersion = gameVersionService.getByPK(versionId);
			if (gameVersion == null) {
				log.error("版本基本包不存在");
				throw new BusinessException(12, "版本基本包不存在");
			}
			ChannelManageVo parentChannel = channelManageService.getVOByPK(parentChlId);
			if (parentChannel == null) {
				log.error("主渠道不存在， parentChlId：{}", parentChlId);
				throw new BusinessException(13, "主渠道不存在");
			}
			Integer platform = parentChannel.getPlatform();
			if (!PlatformTypeEnum.GDT.getValue().equals(String.valueOf(platform))
				&& !PlatformTypeEnum.BD.getValue().equals(String.valueOf(platform))) {
				req.setGdtAppid(null);
			}
			//
			ChannelManageVo sonChannel = channelManageService.getDefaultSonVO(parentChlId);
			if (sonChannel == null) {
				log.error("默认子渠道不存在");
				throw new BusinessException(14, "默认子渠道不存在");
			}
			String chlCode = sonChannel.getChncode();

			List<String> packCodeList = new ArrayList<>();
			// 拼接  “渠道包编码”
			String packCode = chlCode + ChannelConstants.CHANNEL_PACK_SEP + gameId + ChannelConstants.CHANNEL_PACK_SEP + 1;
			packCodeList.add(packCode);

			Long appId = req.getAppId();
			String appName = req.getAppName();
			Integer reportType = req.getReportType();

			if (appId != null && Objects.nonNull(appId)) {
				// 保存 子游戏-平台 上报信息
				WanGameChannelPlatformDO wanGameChannelPlatformDO = new WanGameChannelPlatformDO();
				wanGameChannelPlatformDO.setGameid(gameId.intValue());
				wanGameChannelPlatformDO.setPlatformid(platform);
				wanGameChannelPlatformDO.setAppid(appId);
				wanGameChannelPlatformDO.setAppname(appName);
				if (StringUtils.isNotBlank(req.getGdtAppid())) {
					wanGameChannelPlatformDO.setGdtAppid(req.getGdtAppid());
				}
				gameChannelPlatformService.saveByUK(wanGameChannelPlatformDO);

				// 更新渠道包上报信息
				WanGameChannelInfoDO gameChannelInfoDO = new WanGameChannelInfoDO();
				gameChannelInfoDO.setChl(packCode);
				gameChannelInfoDO.setAppid(String.valueOf(appId));
				gameChannelInfoDO.setAppname(appName);
				gameChannelInfoDO.setRtype(reportType);
				wanGameChannelInfoService.updateByChl(gameChannelInfoDO);
			}
			if (reportType != null) {
				// 更新子渠道的上报方式
				sonChannel.setRtype(reportType);
				PromotionChannelDO promotionChannelDO = new PromotionChannelDO();
				promotionChannelDO.setId(sonChannel.getId());
				promotionChannelDO.setRtype(reportType);
				promotionChannelService.updateByPKSelective(promotionChannelDO);
			}

			// 生成主渠道默认包,  生成渠道参数，不做分包了。
			GameChannelPackReq gameChannelPackReq = new GameChannelPackReq();
			gameChannelPackReq.setCreator(req.getCreator());
			gameChannelPackReq.setType(ChannelPackTypeEnum.DEFAULT.getType());
			batchPackAsync(gameVersion, sonChannel, packCodeList, gameChannelPackReq);
		} catch (BusinessException e) {
			log.error("生成渠道分包失败", e);
			throw new BusinessException(1, e.getMessage());
		} catch (Exception e) {
			log.error("生成渠道分包失败", e);
			throw new BusinessException(1, "生成渠道分包失败");
		}
	}

	@Override
	public void update(GameChannelPackUpdateReq req) {
		try {
			Long packId = req.getPackId();
			// 获取分包信息
			WanChannelPackDO wanChannelPackDO = wanChannelPackDOMapper.selectByPrimaryKey(packId);
			if (wanChannelPackDO == null) {
				log.error("分包记录[{}]信息缺失", packId);
				throw new BusinessException(15, "分包记录[packId=" + packId + "]信息缺失");
			}
			ChannelManageVo channelVO = channelManageService.selectChildChlByCode(wanChannelPackDO.getChlCode());
			Long gameId = wanChannelPackDO.getGameId();

			// 子游戏当前版本信息
			WanGameVersionVO game = gameService.selectVersionVOByPK(gameId, wanChannelPackDO.getPackType());
			if (game != null) {
				boolean updateBasePackFlag = true;
				// 父游戏当前版本信息
				ParentGameVersionDO parentGameVersionDO = parentGameVersionService.getLatestByGameId(game.getPgid(), game.getPackType());
				// 子游戏基础包已更新
				if (parentGameVersionDO == null || (game.getVersionCode() != null && game.getVersionCode() >= parentGameVersionDO.getCode())) {
					updateBasePackFlag = false;
				}
				if (updateBasePackFlag) {
					// 更新子游戏基础包目录
					parentGameVersionService.updateBasePack(parentGameVersionDO, gameId);
				}
			}

			game = gameService.selectVersionVOByPK(gameId, wanChannelPackDO.getPackType());
			if (game == null) {
				log.error("游戏信息缺失, gameId={}", gameId);
				throw new BusinessException(11, "游戏不存在");
			}
			Long versionId = game.getVersionId();
			GameVersionDO gameVersion = gameVersionService.getByPK(versionId);

			List<String> packCodeList = new ArrayList<>();
			packCodeList.add(wanChannelPackDO.getCode());

			GameChannelPackReq gameChannelPackReq = new GameChannelPackReq();
			gameChannelPackReq.setCreator(req.getCreator());
			gameChannelPackReq.setType(wanChannelPackDO.getType());
			batchPackAsync(gameVersion, channelVO, packCodeList, gameChannelPackReq);
		} catch (BusinessException e) {
			log.error("更新渠道分包失败", e);
			throw new BusinessException(1, e.getMessage());
		} catch (Exception e) {
			log.error("更新渠道分包失败", e);
			throw new BusinessException(1, "更新渠道分包失败");
		}
	}

	/**
	 * 生成渠道分包
	 *
	 * @param adId 广告ID
	 */
	@Override
	public WanChannelPackVO pack(Long adId, Integer userId, Integer appType) {
		try {
			// 获取广告信息、渠道信息
			AdvertiserMonitorInfoDO adInfo = adService.selectByPrimaryKey(adId);
			if (adInfo == null) {
				log.error("广告[adId={}]信息缺失", adId);
				throw new BusinessException(15, "广告[adId=" + adId + "]信息缺失");
			}
			ChannelManageVo channelVO = channelManageService.selectChildChlByCode(adInfo.getChildChl());
			Long gameId = adInfo.getChildGid();
			WanGameVersionVO game = gameService.selectVersionVOByPK(gameId, appType);
			if (game == null) {
				log.error("游戏信息缺失, gameId={}", gameId);
				throw new BusinessException(11, "游戏不存在");
			}
			Long versionId = game.getVersionId();
			GameVersionDO gameVersion = gameVersionService.getByPK(versionId);

			List<Long> adIdList = new ArrayList<>();
			adIdList.add(adId);

			Map<Long, WanChannelPackVO> resultMap = this.pack(adInfo, gameVersion, channelVO, adIdList, userId);
			return resultMap.get(adId);
		} catch (BusinessException e) {
			log.error("生成渠道分包失败", e);
			throw new BusinessException(1, e.getMessage());
		} catch (Exception e) {
			log.error("生成渠道分包失败", e);
			throw new BusinessException(1, "生成渠道分包失败");
		}
	}

	@Override
	public Map<Long, WanChannelPackVO> pack(AdvertiserMonitorInfoDO adInfo, GameVersionDO gameVersion, ChannelManageVo channelVO, List<Long> adIdList, Integer userId) {
		Map<Long, WanChannelPackVO> adidPackVOMap = new HashMap<>();
		try {
			if (gameVersion == null) {
				log.error("游戏信息缺失");
				throw new BusinessException(11, "版本基本包不存在");
			}
			if (channelVO == null) {
				log.error("渠道不存在");
				throw new BusinessException(12, "渠道不存在");
			}
			GameChannelPackReq req = new GameChannelPackReq();
			req.setCreator(String.valueOf(userId));
			String chlCode = channelVO.getChncode();
			Long gameId = gameVersion.getGameId();
			List<String> packCodeList = new ArrayList<>();

			Map<String, Long> packCodeAdidMap = new HashMap<>();

			if (StringUtils.isNotBlank(chlCode) && CollectionUtils.isNotEmpty(adIdList)) {
				adIdList.forEach(adId -> {
					// 拼接  “渠道包编码”
					String packCode = chlCode + ChannelConstants.CHANNEL_PACK_SEP + gameId + ChannelConstants.CHANNEL_PACK_SEP + adId;
					packCodeList.add(packCode);
					packCodeAdidMap.put(packCode, adId);
				});
			}
			List<WanChannelPackVO> wanChannelPackVOList = batchPackSync(gameVersion, channelVO, packCodeList, req);
			wanChannelPackVOList.forEach(wanChannelPackVO -> {
				String packCode = wanChannelPackVO.getCode();
				Long adId = packCodeAdidMap.get(packCode);
				// 生成监测链接
				String detectionUrl = getDetectionUrl(adInfo, wanChannelPackVO, channelVO);
				// 生成展示监测链接
				String displayTrackUrl = getDisplayTrackUrl(adInfo, wanChannelPackVO, channelVO);
				// 生成星图监测链接
				String xtMonitorUrl = getXtMonitorUrl(adInfo, wanChannelPackVO, channelVO);
				wanChannelPackVO.setXtMonitorUrl(xtMonitorUrl);
				wanChannelPackVO.setDetectionUrl(detectionUrl);
				wanChannelPackVO.setDisplayMonitorUrl(displayTrackUrl);
				adidPackVOMap.put(adId, wanChannelPackVO);
			});
			return adidPackVOMap;
		} catch (BusinessException e) {
			log.error("生成渠道分包失败", e);
			throw new BusinessException(1, e.getMessage());
		} catch (Exception e) {
			log.error("生成渠道分包失败", e);
			throw new BusinessException(1, "生成渠道分包失败");
		}
	}

	/**
	 * @description: 生成星图的监测地址
	 * @author yuwenfeng
	 * @date 2021/12/14 10:55
	 */
	@Override
	public String getXtMonitorUrl(AdvertiserMonitorInfoDO record, WanChannelPackVO wanChannelPackVO, ChannelManageVo channelVO) {
		try {
			String code = wanChannelPackVO.getCode();
			Long gameId = wanChannelPackVO.getGameId();
			//根据分包编码获取推广方式
			WanGameChannelInfoDO wanGameChannelInfo = wanGameChannelInfoService.getByChl(code);
			if (null != wanGameChannelInfo && null != wanGameChannelInfo.getSpreadType() && 2 == wanGameChannelInfo.getSpreadType()) {
				Map<String, String> params = new HashMap<>();
				params.put("advertId", String.valueOf(record.getAdid()));
				params.put("adaccount", record.getAdvertiserId());
				params.put("gameid", String.valueOf(gameId));
				if (StringUtils.isNotBlank(code)) {
					code = code.replaceAll("_", "").replaceAll("-", "");
				}
				params.put("ptype", code);
				params.put("spreadType", String.valueOf(wanGameChannelInfo.getSpreadType()));
				params.put("settleType", wanGameChannelInfo.getSettleType());
				String template = channelPackConfig.getApi_v3_detection_domain() + "/toutiao/click_api/${ptype}/${gameid}/${adaccount}/${advertId}/${spreadType}/${settleType}?os=__OS__&ua=__UA__&ip=__IP__&model=__MODEL__&demand_id=__DEMAND_ID__&order_id=__ORDER_ID__&item_id=__ITEM_ID__&callback_param=__CALLBACK_PARAM__&callback_url=__CALLBACK_URL__";
				StringSubstitutor sub = new StringSubstitutor(params);
				return sub.replace(template);
			}
		} catch (Exception e) {
			log.warn("获取星图监测链接失败", e);
		}
		return "";
	}

	/**
	 * 调用文件服务器上的打包服务(同步)
	 *
	 * @param gameVersion  子游戏基础包
	 * @param channelVO    子渠道
	 * @param packCodeList 分包编码
	 * @return
	 */
	@Override
	public List<WanChannelPackVO> batchPackSync(GameVersionDO gameVersion, ChannelManageVo channelVO, List<String> packCodeList, GameChannelPackReq req) {
		List<WanChannelPackVO> packList = new ArrayList<>();
		// 创建线程池对象
		// 线程并发度
		ExecutorService service = Executors.newFixedThreadPool(PackConstants.CHANNEL_PACK_THREAD_FIXED_NUM);
		try {
			if (gameVersion == null) {
				log.error("游戏信息缺失");
				throw new BusinessException(11, "游戏不存在");
			}
			if (channelVO == null || CollectionUtils.isEmpty(packCodeList)) {
				log.error("渠道信息缺失");
				throw new BusinessException(12, "渠道信息不存在");
			}
			String remark = req.getRemark();
			String creator = req.getCreator();
			Long gameId = gameVersion.getGameId();
			// 获取主渠道信息
			ChannelManageVo parentChl = channelManageService.getVOByPK(channelVO.getPid());
			Integer platform = parentChl.getPlatform();

			// 获取游戏-平台上报信息
			WanGameChannelPlatformDO wanGameChannelPlatformDO = gameChannelPlatformService.getByPlatformAndGameid(platform, gameId.intValue(), null);

			// 分批处理
			List<Future<List<WanChannelPackVO>>> futureList = new ArrayList<>();
			// 记录分包编码和分包记录ID对应关系
			Map<String, Long> packCodePackIdMap = new HashMap<>();
			List<String> packCodeListTmp = new ArrayList<>();
			for (String packCode : packCodeList) {
				// 创建分包记录
				WanChannelPackDO wanChannelPackDO = new WanChannelPackDO();
				wanChannelPackDO.setGameId(gameId);
				wanChannelPackDO.setVersionId(gameVersion.getVersionId());
				wanChannelPackDO.setCode(packCode);
				wanChannelPackDO.setChlCode(channelVO.getChncode());
				wanChannelPackDO.setPath("");
				wanChannelPackDO.setCreator(creator);
				wanChannelPackDO.setStatus(ChannelPackStatusEnum.GENERATING.getStatus());
				save(wanChannelPackDO);

				packCodeListTmp.add(packCode);
				packCodePackIdMap.put(packCode, wanChannelPackDO.getPackId());

				if (packCodeListTmp.size() >= PackConstants.CHANNEL_PACK_BATCH_NUM) {
					SyncPackThread syncPackThread = new SyncPackThread(gameVersion, parentChl, channelVO, wanGameChannelPlatformDO, packCodeListTmp, remark, creator);
					Future<List<WanChannelPackVO>> futureTmp = service.submit(syncPackThread);
					futureList.add(futureTmp);
					packCodeListTmp = new ArrayList<>();
				}
			}
			if (packCodeListTmp.size() > 0) {
				SyncPackThread syncPackThread = new SyncPackThread(gameVersion, parentChl, channelVO, wanGameChannelPlatformDO, packCodeListTmp, remark, creator);
				Future<List<WanChannelPackVO>> futureTmp = service.submit(syncPackThread);
				futureList.add(futureTmp);
			}
			// 组装多线程结果
			for (Future<List<WanChannelPackVO>> futureTmp : futureList) {
				packList.addAll(futureTmp.get());
			}
			// 补充packId信息
			for (WanChannelPackVO packVO : packList) {
				String packCode = packVO.getCode();
				Long packId = packCodePackIdMap.get(packCode);
				packVO.setPackId(packId);
			}
			return packList;
		} catch (BusinessException e) {
			log.error("生成渠道分包失败", e);
			throw new BusinessException(12, e.getMessage());
		} catch (Exception e) {
			log.error("生成渠道分包失败", e);
			throw new BusinessException(12, "生成渠道分包失败");
		} finally {
			//注意：submit方法调用结束后，程序并不终止，是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
			//关闭线程池
			service.shutdown();
		}
	}

	/**
	 * 生成广告的点击监测地址
	 *
	 * @param record
	 * @param wanChannelPackVO
	 * @param channelVO
	 * @return
	 */
	@Override
	public String getDetectionUrl(AdvertiserMonitorInfoDO record, WanChannelPackVO wanChannelPackVO, ChannelManageVo channelVO) {
		try {
			String code = wanChannelPackVO.getCode();
			Long gameId = wanChannelPackVO.getGameId();
			// 渠道包信息
			WanGameChannelInfoDO gameChannelInfoDB = wanGameChannelInfoService.getByChl(code);
			Integer platform = gameChannelInfoDB.getPlatform();

			Map<String, String> params = new HashMap<>();
			params.put("advertId", String.valueOf(record.getAdid()));
			params.put("adaccount", record.getAdvertiserId());
			params.put("gameid", String.valueOf(gameId));
			if (StringUtils.isNotBlank(code)) {
				code = code.replaceAll("_", "").replaceAll("-", "");
			}
			params.put("ptype", code);
			return getDetectionUrl(platform, params, channelPackConfig.getApi_v3_detection_domain());
		} catch (Exception e) {
			log.warn("获取分包监测链接失败", e);
		}
		return "";
	}

	private String getDetectionUrl(Integer platform, Map<String, String> params, String api_v3_detection_domain) {
		String template = api_v3_detection_domain;
		if (platform != null && template != null) {
			switch (platform) {
				case 1:
				case 6:
					template += ToutiaoConfig.click_track_url_template;
					break;
				case 7:
					template += "/uc/click_api/${ptype}/${gameid}/${advertId}?idfa1={IDFA1}&imeis1={IMEI_SUM1}&oaid={OAID}&ts={TS}&callurl={CALLBACK_URL}&acid={ACID}&gid={GID}&aid={AID}&cid={CID}&macs={MAC_SUM}&andrs1={ANDROIDID_SUM1}&ua={UA}&os={OS}&ip={IP}";
					break;
				case 8:
//					template += "/gdt/click_api?ptype=${ptype}&gameid=${gameid}&advert_id=${advertId}";
					// 剩下有效且未拼接的宏参数  &rta_exp_id=_RTA_EXP_ID__&boost_exp_info=__BOOST_EXP_INFO__&channel_package_id=__CHANNEL_PACKAGE_ID__&page_url=__PAGE_URL__&rta_user_weight_factor=__RTA_USER_WEIGHT_FACTOR__&rta_dsp_tag=__RTA_DSP_TAG__&ad_platform_type=__AD_PLATFORM_TYPE__&rta_valid_features=__RTA_VALID_FEATURES__&rta_out_target_id=__RTA_OUT_TARGET_ID__&ad_id=__AD_ID__&ipv6=__IPV6__&model=__MODEL__&rta_cpc_bi=__RTA_CPC_BID__&encrypted_position_id=__ENCRYPTED_POSITION_ID__&rta_cpa_price=__RTA_CPA_PRICE__&device_os_version=__DEVICE_OS_VERSION__&qaid_caa=__QAID_CAA__&promoted_object_type=__PROMOTED_OBJECT_TYPE__&pkam_exp_ids=__PKAM_EXP_IDS__&only_valuable_click=__ONLY_VALUABLE_CLICK__&site_set_name=__SITE_SET_NAME__&agency_id=__AGENCY_ID__&universal_link=__UNIVERSAL_LINK__&medium_industry=__MEDIUM_INDUSTRY__&rta_trace_id=__RTA_TRACE_ID__&billing_even=__BILLING_EVENT__&rta_user_weight=__RTA_USER_WEIGHT__&rta_target_id=__RTA_TARGET_ID___&boost_model_id=__BOOST_MODEL_ID__
					template += GdtConfig.click_track_url_template;
					break;
				case 9:
					template += "/baidu/click_api/${ptype}/${gameid}/${advertId}?click_id={{CLICK_ID}}&userid={{USER_ID}}&aid={{IDEA_ID}}&pid={{PLAN_ID}}&uid={{UNIT_ID}}&idfa={{IDFA}}&imei_md5={{IMEI_MD5}}&oaid={{OAID}}&oaid_md5={{OAID_MD5}}&android_id_md5={{ANDROID_ID_MD5}}&mac_md5={{MAC_MD5}}&ip={{IP}}&ua={{UA}}&os={{OS}}&ts={{TS}}&size={{SIZE}}&callback_url={{CALLBACK_URL}}&sign={{SIGN}}";
					break;
				default:
					template = "";
			}
		}
		return StrSubstitutor.replace(template, params);
	}

	/**
	 * 获取展示监测链接
	 * @param record
	 * @param wanChannelPackVO
	 * @param channelVO
	 * @return
	 */
	@Override
	public String getDisplayTrackUrl(AdvertiserMonitorInfoDO record, WanChannelPackVO wanChannelPackVO, ChannelManageVo channelVO) {
		try {
			String code = wanChannelPackVO.getCode();
			Long gameId = wanChannelPackVO.getGameId();
			// 渠道包信息
			WanGameChannelInfoDO gameChannelInfoDB = wanGameChannelInfoService.getByChl(code);
			Integer platform = gameChannelInfoDB.getPlatform();

			Map<String, String> params = new HashMap<>();
			params.put("advertId", String.valueOf(record.getAdid()));
			params.put("adaccount", record.getAdvertiserId());
			params.put("gameid", String.valueOf(gameId));
			if (StringUtils.isNotBlank(code)) {
				code = code.replaceAll("_", "").replaceAll("-", "");
			}
			params.put("ptype", code);
			return getDisplayTrackUrl(platform, params, channelPackConfig.getApi_v3_detection_domain());
		} catch (Exception e) {
			log.warn("获取分包监测链接失败", e);
		}
		return "";
	}
	private String getDisplayTrackUrl(Integer platform, Map<String, String> params, String api_v3_detection_domain) {
		String template = api_v3_detection_domain;
		if (platform != null && template != null) {
			switch (platform) {
				case 1:
				case 6:
					template += ToutiaoConfig.display_track_url_template;
					break;
				case 7:
					template += "/uc/click_api/${ptype}/${gameid}/${advertId}?idfa1={IDFA1}&imeis1={IMEI_SUM1}&oaid={OAID}&ts={TS}&callurl={CALLBACK_URL}&acid={ACID}&gid={GID}&aid={AID}&cid={CID}&macs={MAC_SUM}&andrs1={ANDROIDID_SUM1}&ua={UA}&os={OS}&ip={IP}";
					break;
				case 8:
//					template += "/gdt/click_api?ptype=${ptype}&gameid=${gameid}&advert_id=${advertId}";
					// 剩下有效且未拼接的宏参数  &rta_exp_id=_RTA_EXP_ID__&boost_exp_info=__BOOST_EXP_INFO__&channel_package_id=__CHANNEL_PACKAGE_ID__&page_url=__PAGE_URL__&rta_user_weight_factor=__RTA_USER_WEIGHT_FACTOR__&rta_dsp_tag=__RTA_DSP_TAG__&ad_platform_type=__AD_PLATFORM_TYPE__&rta_valid_features=__RTA_VALID_FEATURES__&rta_out_target_id=__RTA_OUT_TARGET_ID__&ad_id=__AD_ID__&ipv6=__IPV6__&model=__MODEL__&rta_cpc_bi=__RTA_CPC_BID__&encrypted_position_id=__ENCRYPTED_POSITION_ID__&rta_cpa_price=__RTA_CPA_PRICE__&device_os_version=__DEVICE_OS_VERSION__&qaid_caa=__QAID_CAA__&promoted_object_type=__PROMOTED_OBJECT_TYPE__&pkam_exp_ids=__PKAM_EXP_IDS__&only_valuable_click=__ONLY_VALUABLE_CLICK__&site_set_name=__SITE_SET_NAME__&agency_id=__AGENCY_ID__&universal_link=__UNIVERSAL_LINK__&medium_industry=__MEDIUM_INDUSTRY__&rta_trace_id=__RTA_TRACE_ID__&billing_even=__BILLING_EVENT__&rta_user_weight=__RTA_USER_WEIGHT__&rta_target_id=__RTA_TARGET_ID___&boost_model_id=__BOOST_MODEL_ID__
					template += GdtConfig.display_track_url_template;
					break;
				case 9:
					template = "";
					//template += "/baidu/click_api/${ptype}/${gameid}/${advertId}?click_id={{CLICK_ID}}&userid={{USER_ID}}&aid={{IDEA_ID}}&pid={{PLAN_ID}}&uid={{UNIT_ID}}&idfa={{IDFA}}&imei_md5={{IMEI_MD5}}&oaid={{OAID}}&oaid_md5={{OAID_MD5}}&android_id_md5={{ANDROID_ID_MD5}}&mac_md5={{MAC_MD5}}&ip={{IP}}&ua={{UA}}&os={{OS}}&ts={{TS}}&size={{SIZE}}&callback_url={{CALLBACK_URL}}&sign={{SIGN}}";
					break;
				default:
					template = "";
			}
		}
		return StrSubstitutor.replace(template, params);
	}




	@Override
	public List<WanChannelPackVO> latestList(WanChannelPackReq req) {
		if (StringUtils.isNotBlank(req.getPackUserid())) {
			// 获取主渠道信息
			ChannelManageVo channelVO = channelManageService.selectParentChlByCode(req.getParentCode());
			if (Objects.isNull(channelVO)) {
				return newArrayList();
			}

			ChannelManageDto channel = new ChannelManageDto();
			channel.setPackUserid(SecurityUtils.getUser().getId());
			channel.setPid(channelVO.getId());
			// 根据父渠道ID 获取子渠道编码
			ChannelManageVo channelManageVo = channelManageDOMapperExt.querySonChlBylist(channel);
			if (Objects.isNull(channelManageVo)) {
				return newArrayList();
			}

			req.setChlCode(channelManageVo.getChncode());
			List<Integer> userList = Lists.newLinkedList();
			userList.add(SecurityUtils.getUser().getId());
			req.setUserList(userList);

			req.setPlatformId(String.valueOf(channelManageVo.getPlatform()));
			// 联调转化分包下拉列，排除已使用的分包
			//req.setIsPack("true");
		} else {
			List<Integer> userList = adRoleUserService.getOwnerRoleUserIds();
			//目前根据平台角色查询分包，不根据角色
			//req.setPackUserid(String.valueOf(SecurityUtils.getUser().getId()));
			req.setUserList(userList);
		}
		String weChatGameIds = yunYingProperties.getWeChatGameIds();
		List<String> gameIds = Arrays.asList(weChatGameIds.split(","));
		WanGameDO wanGameDO = gameService.getById(req.getGameId());
		// 查询分包
		List<WanChannelPackVO> wanChannelPackVOList = wanChannelPackDOMapper.latestList(req);
		// 微信游戏白名单
		if (gameIds.contains(String.valueOf(wanGameDO.getPgid()))) {
			return wanChannelPackVOList;
		}
		// 过滤媒体未通过的包
		if (CollectionUtils.isNotEmpty(wanChannelPackVOList)) {
			List<String> packCodes = wanChannelPackVOList.stream().map(WanChannelPackVO::getCode).collect(Collectors.toList());
			// 获取广点通 gdt_channel_packages 表同步状态
			R<List<GdtChannelPackage>> resultChannelPackageList = remoteConvertService.getPackageList(new GdtChannelPackageReq().setAppChlList(packCodes), SecurityConstants.FROM_IN);

			Iterator<WanChannelPackVO> iterator = wanChannelPackVOList.iterator();
			while (iterator.hasNext()) {
				WanChannelPackVO wanChannelPack = iterator.next();

				if (PlatformTypeEnum.GDT.getValue().equals(req.getPlatformId())) {
					int flag = 0;
					// gdt_channel_packages 表同步状态
					List<GdtChannelPackage> gdtChannelPackageList = resultChannelPackageList.getData();
					if (CollectionUtils.isNotEmpty(gdtChannelPackageList)) {
						for (GdtChannelPackage gdtChannelPackage : gdtChannelPackageList) {
							if (wanChannelPack.getCode().equals(gdtChannelPackage.getAppChl())) {
								if (GdtChannelPackageEnum.CHANNEL_PACKAGE_STATUS_PASSED.getValue().equals(gdtChannelPackage.getPackageStatus())) {
									flag++;
								}
							}
						}
					}
					if (flag == 0){
						iterator.remove();
					}
				} else if (PlatformTypeEnum.TT.getValue().equals(req.getPlatformId())) {
					if (!"PUBLISHED".equals(wanChannelPack.getTtStatusStr())) {
						iterator.remove();
					}
				}
			}
		}
		return wanChannelPackVOList;
	}

	/**
	 * 确定包名唯一
	 *
	 * @param codeName
	 * @return
	 */
	@Override
	public List<WanGameChannelInfoDO> queryPkByCodeName(String codeName, String chl) {
		return wanChannelPackDOMapper.queryPkByCodeName(codeName, chl);
	}

	/**
	 * 分包编码 查询对应分包名称
	 *
	 * @param appChlArr
	 * @return
	 */
	@Override
	public List<Map<String, String>> queryAppChlName(List<String> appChlArr) {
		return wanChannelPackDOMapper.queryAppChlName(appChlArr);
	}

	/**
	 * 查询数据报表模块的分包 下拉框
	 *
	 * @param req
	 * @return
	 */
	@Override
	public List<Map<String, String>> queryAppchlList(WanAppChlReq req) {

		return wanChannelPackDOMapper.queryAppchlList(req);
	}

	@Override
	public List<Map<String, String>> queryCpsAppchlList(WanAppChlReq req) {
		return wanChannelPackDOMapper.queryCpsAppchlList(req);
	}

	@Override
	public List<Map<String, String>> queryCpsSubchlList(WanAppChlReq req) {
		return wanChannelPackDOMapper.queryCpsSubchlList(req);
	}

	@Override
	public void cpsPack(GameChannelPackReq req) {
		try {
			Long gameId = req.getGameId();
			WanGameVersionVO game = gameService.selectVersionVOByPK(gameId, req.getPackType());
			if (game == null || game.getVersionId() == null) {
				log.error("游戏基本包不存在");
				throw new BusinessException(11, "游戏基本包不存在");
			}
			req.setCreator(String.valueOf(SecurityUtils.getUser().getId()));
			int channelId = req.getChlId();
			Long versionId = game.getVersionId();
			GameVersionDO gameVersion = gameVersionService.getByPK(versionId);
			if (gameVersion == null) {
				log.error("版本基本包不存在");
				throw new BusinessException(12, "版本基本包不存在");
			}
			ChannelManageVo channelVO = channelManageService.getVOByPK(channelId);
			if (channelVO == null) {
				log.error("渠道不存在");
				throw new BusinessException(13, "渠道不存在");
			}
			String chlCode = channelVO.getChncode();

			List<String> packCodeList = new ArrayList<>();
			for (int i = req.getPackNumFrom(); i <= req.getPackNumTo(); i++) {
				// 拼接  “渠道包编码”
				String packCode = chlCode + ChannelConstants.CHANNEL_PACK_SEP + gameId + ChannelConstants.CHANNEL_PACK_SEP + i;
				packCodeList.add(packCode);
			}
			cpsBatchPackAsync(gameVersion, channelVO, packCodeList, req);
		} catch (BusinessException e) {
			log.error("分包失败", e.getMessage());
			throw new BusinessException(1, e.getMessage());
		} catch (Exception e) {
			log.error("分包失败", e);
			throw new BusinessException(1, "分包失败");
		}
	}

	@Override
	public IPage<WanChannelPackPageVO> cpsPage(WanChannelCpsPackReq req) {
		IPage<WanChannelPackPageVO> page = wanChannelPackDOMapper.cpsPage(req);

		if (page != null && CollectionUtils.isNotEmpty(page.getRecords())) {

			R<List<UserSelectVO>> r = remoteUserService.getUserSelectList();
			R<List<SysUser>> cpsList = remoteUserService.getUserSelectListByTenantId(SecurityConstants.FROM_IN, yunYingProperties.getTenantId());
			Map<String, UserSelectVO> map = new HashMap<>();
			Map<String, SysUser> mapCps = new HashMap<>();
			if (r.getCode() == CommonConstants.SUCCESS) {
				List<UserSelectVO> list = r.getData();
				//map = list.stream().filter(x -> x != null && StringUtils.isNotBlank(x.getRealName())).collect(Collectors.toMap(
				map = list.stream().collect(Collectors.toMap(
						o -> {
							return String.valueOf(o.getUserId());
						}, Function.identity()));
			}

			if (cpsList.getCode() == CommonConstants.SUCCESS){
				List<SysUser> list = cpsList.getData();
				//map = list.stream().filter(x -> x != null && StringUtils.isNotBlank(x.getRealName())).collect(Collectors.toMap(
				mapCps = list.stream().collect(Collectors.toMap(
						o -> {
							return String.valueOf(o.getUserId());
						}, Function.identity()));
			}

			for (WanChannelPackPageVO wanChannelPackVO : page.getRecords()) {
				String creator = wanChannelPackVO.getCreator();
				Long manage = wanChannelPackVO.getManage();
				UserSelectVO user = map.get(creator);
				if (user != null) {
					wanChannelPackVO.setCreatorName(StringUtils.isBlank(user.getRealName()) ? user.getUsername() : user.getRealName());
				} else {
					wanChannelPackVO.setCreatorName(creator);
				}
				if (manage != null){
					SysUser sysUser = mapCps.get(manage.toString());
					if (sysUser != null){
						wanChannelPackVO.setLeadperson(StringUtils.isBlank(sysUser.getRealName()) ? "" : sysUser.getRealName());
					}
				}
				String path = wanChannelPackVO.getPath();
				if (StringUtils.isNotBlank(path)) {
					wanChannelPackVO.setUrl(channelPackConfig.getApp_file_url_root() + path);
				} else {
					wanChannelPackVO.setUrl("");
				}
			}
		}
		return page;
	}

	@Override
	public List<SubChannelInfoDO> queryChlInfoListByAuth(WanAppChlReq req) {
		return wanChannelPackDOMapper.queryChlInfoListByAuth(req);
	}



	private void cpsBatchPackAsync(GameVersionDO gameVersion, ChannelManageVo channelVO, List<String> packCodeList, GameChannelPackReq req) {
		// 创建线程池对象
		ExecutorService service = Executors.newFixedThreadPool(PackConstants.CHANNEL_PACK_THREAD_FIXED_NUM); // 线程并发度
		try {
			if (gameVersion == null) {
				log.error("游戏信息缺失");
				throw new BusinessException(11, "游戏不存在");
			}
			if (channelVO == null || CollectionUtils.isEmpty(packCodeList)) {
				log.error("渠道信息缺失");
				throw new BusinessException(12, "渠道信息不存在");
			}

			String remark = req.getRemark();
			String creator = req.getCreator();
			Long gameId = gameVersion.getGameId();
			// 获取主渠道信息
			ChannelManageVo parentChl = channelManageService.getVOByPK(channelVO.getPid());
			Integer platform = parentChl.getPlatform();

			// 获取游戏-平台上报信息
			WanGameChannelPlatformDO wanGameChannelPlatformDO = gameChannelPlatformService.getByPlatformAndGameid(platform, gameId.intValue(), null);

			// 分批处理
			List<String> packCodeListTmp = new ArrayList<>();
			for (String packCode : packCodeList) {
				// 创建分包记录
				WanChannelPackDO wanChannelPackDO = new WanChannelPackDO();
				wanChannelPackDO.setGameId(gameId);
				wanChannelPackDO.setVersionId(gameVersion.getVersionId());
				wanChannelPackDO.setType(req.getType());
				wanChannelPackDO.setCode(packCode);
				wanChannelPackDO.setChlCode(channelVO.getChncode());
				wanChannelPackDO.setPath("");
				wanChannelPackDO.setCreator(creator);
				wanChannelPackDO.setStatus(ChannelPackStatusEnum.GENERATING.getStatus());
				if (null != req.getSpreadType() && 2 == req.getSpreadType()) {
					wanChannelPackDO.setSpreadType(req.getSpreadType());
					wanChannelPackDO.setSettleType(req.getSettleType());
				}
				save(wanChannelPackDO);

				packCodeListTmp.add(packCode);

				if (packCodeListTmp.size() >= PackConstants.CHANNEL_PACK_BATCH_NUM) {
					GameChannelPackServiceImpl.SyncPackThread syncPackThread = new GameChannelPackServiceImpl.SyncPackThread(gameVersion, parentChl, channelVO, wanGameChannelPlatformDO, packCodeListTmp, remark, creator);
					service.submit(syncPackThread);
					packCodeListTmp = new ArrayList<>();
				}
			}
			if (packCodeListTmp.size() > 0) {
				GameChannelPackServiceImpl.SyncPackThread syncPackThread = new GameChannelPackServiceImpl.SyncPackThread(gameVersion, parentChl, channelVO, wanGameChannelPlatformDO, packCodeListTmp, remark, creator);
				service.submit(syncPackThread);
			}

//			if (wanGameChannelPlatformDO == null) {
//				throw new BusinessException(12, "盘古平台包已生成,但渠道参数未配置");
//			}
//
//			if (1 == platform && wanGameChannelPlatformDO != null && req.getType() != 2) {
//				Integer appid = wanGameChannelPlatformDO.getAppid();
//				// 根据appid 查询对应的应用包id和广告账户
//				TtAppManagement ttAppManagement = ttExtendPackageMapperExt.getPackageIdByAppId(String.valueOf(appid));
//				if (ttAppManagement != null) {
//					// 调用头条API《创建应用分包》及分包列表
//					tTExtendPackage(req, packCodeList, parentChl, wanGameChannelPlatformDO, ttAppManagement);
//				} else {
//					throw new BusinessException(12, "盘古平台包已生成,但应用未同步媒体包未生成");
//				}
//			}
//
//			if (platform == 8) {
//				// 广点通分包 在创建转化步骤
//			} else if (platform == 10) {
//				// 快手没有分包接口 目前是按子游戏维度创建转化时调用快手创建应用的接口
//			}

		} catch (BusinessException e) {
			log.error("生成渠道分包失败", e);
			throw new BusinessException(12, e.getMessage());
		} catch (Exception e) {
			log.error("生成渠道分包失败", e);
			throw new BusinessException(12, "生成渠道分包失败");
		} finally {
			//注意：submit方法调用结束后，程序并不终止，是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
			//关闭线程池
			service.shutdown();
		}
	}

	/**
	 * 创建广点通应用分包
	 * @param wanGameChannelPlatformDO
	 * @param packCodeList
	 * @return
	 */
	public R gdtExtendPackage(WanGameChannelPlatformDO wanGameChannelPlatformDO, List<String> packCodeList){
		// 获取创建者广告账户和appid
		GdtApp gdtApp = gdtAppMapper.selectOne(Wrappers.<GdtApp>lambdaQuery()
				.eq(GdtApp::getPromotedObjectId, wanGameChannelPlatformDO.getGdtAppid())
				.eq(GdtApp::getDeleted, 0)
				.orderByAsc(GdtApp::getCreatedTime)
				.last("LIMIT 1"));
		if (Objects.isNull(gdtApp)){
			log.error("未获取广点通到AppId信息:" + wanGameChannelPlatformDO.getGdtAppid());
			return R.failed("未获取广点通到AppId信息:" + wanGameChannelPlatformDO.getGdtAppid());
		}

		GdtChannelPackageReq channelPackage = new GdtChannelPackageReq();
		channelPackage.setAdAccount(String.valueOf(gdtApp.getAccountId()));
		channelPackage.setUnionAppId(String.valueOf(gdtApp.getPromotedObjectId()));
		channelPackage.setAppChlList(packCodeList);
		// 远程调用创建应用包
		log.info(">>>广点通创建应用包.params:" + JSON.toJSONString(channelPackage));
		R result = remoteConvertService.extendPackageAdd(channelPackage, SecurityConstants.FROM_IN);
		log.info(">>>广点通创建应用包.result:" + result);
		return result;
	}


}
